package com.cpit.qualitycontrol.web.controller;

import com.cpit.platform.common.constant.LogConstant;
import com.cpit.platform.common.entity.Result;
import com.cpit.platform.common.entity.TableDataQueryCondition;
import com.cpit.platform.common.entity.TableJsonData;
import com.cpit.platform.common.excel.ExportExcel;
import com.cpit.platform.common.excel.ImportExcel;
import com.cpit.platform.common.exception.CodeException;
import com.cpit.platform.common.utils.ExcelImportUtils;
import com.cpit.platform.common.utils.ResultUtil;
import com.cpit.platform.dto.OperLog;
import com.cpit.platform.log.LogOperator;
import com.cpit.platform.service.OperLogService;
import com.cpit.qualitycontrol.common.constant.LogConstantEx;
import com.cpit.qualitycontrol.entity.Device;
import com.cpit.qualitycontrol.entity.DeviceType;
import com.cpit.qualitycontrol.entity.Project;
import com.cpit.qualitycontrol.service.DeviceService;
import com.cpit.qualitycontrol.service.DeviceTypeService;
import com.cpit.qualitycontrol.service.ProductLineService;
import com.cpit.qualitycontrol.service.ProjectService;
import com.cpit.qualitycontrol.service.impl.ServiceUtils;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping(value = "/Device")
public class DeviceController {

    private final String loggerName = this.getClass().getName();

    @Autowired
    private DeviceService deviceService = null;
    @Autowired
    private DeviceTypeService deviceTypeService;
    @Autowired
    private ProjectService projectService;
    @Autowired
    private OperLogService operLogService;

    @ApiOperation(value = "查询所有设备", notes = "无条件查询设备信息，返回对象List")
    @ResponseBody
    //@PreAuthorize("hasAuthority('queryDevice')")
    @RequestMapping(value = "/queryAllDevice", method = RequestMethod.GET)
    public List<Device> queryAllDevice() {
        return this.deviceService.findAll();
    }

    /*@ApiOperation(value="查询设备列表", notes="通过条件查询设备信息，返回List方式" +
            "支持模糊查询。")
    @ResponseBody
    //@PreAuthorize("hasAuthority('queryDevice')")
    @RequestMapping(value="/queryDevice", method = RequestMethod.GET)
    public List<Device> queryDevice(
            @ApiParam(value = "当前页数， 以1开始。") @RequestParam(value = "page", required = false, defaultValue = "1") int page,
            @ApiParam(value = "当前页的内容长度。") @RequestParam(value = "size", required = false, defaultValue = "20") int size,
            @ApiParam(value = "排序方式：asc为升序，desc为降序。") @RequestParam(value = "order", required = false, defaultValue = "asc") String order,
            @ApiParam(value = "排序字段，与dto中声明的字段名称一致。") @RequestParam(value = "orderBy", required = false, defaultValue = "id") String orderBy,
            @ApiParam(value = "模糊搜索的字符串，根据角色的名称字段进行模糊匹配。") @RequestParam(value = "pattern", required = false, defaultValue = "") String pattern) {
        return this.deviceService.findByCondition(page, size, order, orderBy, pattern);
    }*/

    @ApiOperation(value = "查询设备", notes = "通过条件查询设备信息，返回jqGrid分页JSON形式" +
            "支持分页查询，支持模糊查询。")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "cond", value = "JqGrid的条件对象，由JqGrid根据js参数自动封装。", required = true, dataType = "TableDataQueryContidion")
    })
    @SuppressWarnings("rawtypes")
    @ResponseBody
    //@PreAuthorize("hasAuthority('queryDevice')")
    @RequestMapping(value = "/queryDeviceJqg", method = RequestMethod.GET)
    public TableJsonData queryDeviceJqg(TableDataQueryCondition cond) {
        TableJsonData tjd = this.deviceService.findByConditionJqg(cond);
        return tjd;
    }

    @ApiOperation(value = "创建设备", notes = "用户手工指定设备并创建，" +
            "需要手动输入设备名称和描述信息，设备名称不能重复。")
    @ApiImplicitParam(name = "data", value = "设备对象", required = true, dataType = "Device")
    @ResponseBody
    //@PreAuthorize("hasAuthority('createDevice')")
    @RequestMapping(value = "/createDevice", method = RequestMethod.POST)
    public Result<Device> createDevice(@RequestBody Device data) {
//        if (StringUtils.isEmpty(data.getId()) || StringUtils.isEmpty(data.getName())
//                || StringUtils.isEmpty(data.getAddrs()) || StringUtils.isEmpty(data.getNote())
//                || StringUtils.isEmpty(data.getSource()) || StringUtils.isEmpty(data.getVendor())
//                || StringUtils.isEmpty(data.getVersion())
//                ) {
//            return ResultUtil.error(Result.CODE_FAILED, "设备信息不全，请补充！");
//        }
        try {
            Device retDevice = this.deviceService.create(data);
            writeLog(LogConstant.OPER_CREATE, Result.CODE_SUCCESS, "设备：" + retDevice.getName());
            return new Result<>(Result.CODE_SUCCESS, "", retDevice);
        } catch (Exception e) {
            LogOperator.err(loggerName, "createDevice error", e);
            writeLog(LogConstant.OPER_CREATE, Result.CODE_FAILED, e.getMessage());
            return new Result<>(Result.CODE_FAILED, e.getMessage(), data);
        }
    }

    @ApiOperation(value = "修改设备", notes = "用户手工指定设备并修改，" +
            "需要手动输入设备名称和描述信息，设备名称不能重复。")
    @ApiImplicitParam(name = "data", value = "设备对象", required = true, dataType = "Device")
    @ResponseBody
    //@PreAuthorize("hasAuthority('modifyDevice')")
    @RequestMapping(value = "/modifyDevice", method = RequestMethod.POST)
    public Result<Device> modifyDevice(@RequestBody Device data) {
//        if (StringUtils.isEmpty(data.getId()) || StringUtils.isEmpty(data.getName())
//                || StringUtils.isEmpty(data.getAddrs()) || StringUtils.isEmpty(data.getNote())
//                || StringUtils.isEmpty(data.getSource()) || StringUtils.isEmpty(data.getVendor())
//                || StringUtils.isEmpty(data.getVersion())
//                ) {
//            return ResultUtil.error(Result.CODE_FAILED, "设备信息不全，请补充！");
//        }
        try {
            Device retDevice = this.deviceService.modify(data);
            writeLog(LogConstant.OPER_MODIFY, Result.CODE_SUCCESS, "设备：" + retDevice.getName());
            return new Result<>(Result.CODE_SUCCESS, "", retDevice);
        } catch (Exception e) {
            LogOperator.err(loggerName, "modifyDevice error", e);
            writeLog(LogConstant.OPER_MODIFY, Result.CODE_FAILED, e.getMessage());
            return new Result<>(Result.CODE_FAILED, e.getMessage(), data);
        }
    }

    @ApiOperation(value = "删除设备", notes = "在系统中删除指定设备")
    @ApiImplicitParam(name = "data", value = "设备对象", required = true, dataType = "Device")
    @ResponseBody
    //@PreAuthorize("hasAuthority('deleteDevice')")
    @RequestMapping(value = "/deleteDevice", method = RequestMethod.DELETE)
    public Result<Device> deleteDevice(@RequestBody Device data) {
        try {
            this.deviceService.delete(data);
            writeLog(LogConstant.OPER_DELETE, Result.CODE_SUCCESS, "设备：" + data.getName());
            return new Result<>(Result.CODE_SUCCESS, "", data);
        } catch (Exception e) {
            LogOperator.err(loggerName, "deleteDevice error", e);
            writeLog(LogConstant.OPER_DELETE, Result.CODE_FAILED, e.getMessage());
            return new Result<>(Result.CODE_FAILED, e.getMessage(), data);
        }
    }

    @ApiOperation(value = "查询设备列表", notes = "通过条件查询设备信息，返回List方式" +
            "支持模糊查询。")
    @ResponseBody
    //@PreAuthorize("hasAuthority('queryProcessRule')")
    @RequestMapping(value = "/queryDevice", method = RequestMethod.GET)
    public List<Device> queryDevice(
            @ApiParam(value = "当前页数， 以1开始。") @RequestParam(value = "page", required = false, defaultValue = "1") int page,
            @ApiParam(value = "当前页的内容长度。") @RequestParam(value = "size", required = false, defaultValue = "20") int size,
            @ApiParam(value = "排序方式：asc为升序，desc为降序。") @RequestParam(value = "order", required = false, defaultValue = "asc") String order,
            @ApiParam(value = "排序字段，与dto中声明的字段名称一致。") @RequestParam(value = "orderBy", required = false, defaultValue = "id") String orderBy,
            @ApiParam(value = "產品线Id") @RequestParam(value = "productLineId", required = false, defaultValue = "") Long productLineId,
            @ApiParam(value = "项目名称") @RequestParam(value = "projectName", required = false) String projectName,
            @ApiParam(value = "设备类型ID") @RequestParam(value = "deviceTypeId", required = false) Long deviceTypeId,
            @ApiParam(value = "设备编号") @RequestParam(value = "deviceNum", required = false) String deviceNum,
            @ApiParam(value = "设备地址") @RequestParam(value = "addr", required = false) String addr) {
        return this.deviceService.findByCondition(page, size, order, orderBy, productLineId, projectName, deviceTypeId, deviceNum, addr);
    }

    @ApiOperation(value = "导入设备列表", notes = "向系统中导入设备列表")
    //@PreAuthorize("hasAuthority('createDevice')")
    @ResponseBody
    @RequestMapping(value = "/importDeviceList", method = RequestMethod.POST, consumes = "multipart/form-data")
    public Result<Integer> importDevice(
            @ApiParam(value = "设备信息Excel表格。", required = true) @RequestParam("fileupload") MultipartFile file, HttpServletRequest request) {

        String[] title = {"设备编号*", "设备名称*", "设备地址", "设备类型*", "设备型号", "设备厂家", "经度", "纬度", "项目归属*", "产品线*", "备注"};
        int headerNum = 1;
        int sheetIndex = 0;
        StringBuilder sb = new StringBuilder();
        if (file == null) {
            throw new CodeException(Result.CODE_FAILED, "文件不能为空！");
        }
        String fileName = file.getOriginalFilename();
        // //验证文件名是否合格
        if (!ExcelImportUtils.validateExcel(fileName)) {
            throw new CodeException(Result.CODE_FAILED, "文件必须是excel格式！");
        }
        long size = file.getSize();
        if (StringUtils.isEmpty(fileName) || size == 0) {
            throw new CodeException(Result.CODE_FAILED, "文件不能为空！");
        }

        // 创建导入Excel对象
        ImportExcel ei = null;
        List<Device> deviceList = null;
        boolean partSuccess = false;
        try {
            ei = new ImportExcel(file, headerNum, sheetIndex);

            StringBuilder errorSB = new StringBuilder();
            String errorStr = ei.validateHead(title);
            errorSB.append(errorStr);

            if (!StringUtils.isEmpty(errorSB.toString())) {
                throw new CodeException("1", "文件格式错误！");
            }

            // 获取传入Excel文件的数据，根据传入参数类型，自动转换为对象
            deviceList = ei.getDataList(Device.class);
            //补充projectid， devicetypeid，productlineId
            Map<String, Project> projectMap = new HashMap<String, Project>();
            for (Project project : projectService.findAll()
            ) {
                projectMap.put(project.getName(), project);
            }
            Map<String, Long> deviceTypeMap = new HashMap<String, Long>();
            for (DeviceType deviceType : deviceTypeService.findAll()
            ) {
                deviceTypeMap.put(deviceType.getName(), deviceType.getId());
            }

            // 插入数据库
            for (Device device : deviceList) {
                try {
                    if (StringUtils.isEmpty(device.getId()) && StringUtils.isEmpty(device.getName()) && StringUtils.isEmpty(device.getDeviceTypeName())
                            && StringUtils.isEmpty(device.getProjectName()) && StringUtils.isEmpty(device.getProductLineName())
                            && StringUtils.isEmpty(device.getAddrs()) && StringUtils.isEmpty(device.getNote())
                            && StringUtils.isEmpty(device.getVendor()) && StringUtils.isEmpty(device.getVersion())) {
                        continue;
                    }

                    if (StringUtils.isEmpty(device.getId()) || StringUtils.isEmpty(device.getName()) || StringUtils.isEmpty(device.getDeviceTypeName())
                            || StringUtils.isEmpty(device.getProjectName()) || StringUtils.isEmpty(device.getProductLineName())
                    ) {
                        throw new Exception("设备信息不全，请补充！");
                    }
                    if (device.getId().length() > 25 || device.getName().length() > 25
                            || !StringUtils.isEmpty(device.getAddrs()) && device.getAddrs().length() > 250 || !StringUtils.isEmpty(device.getNote()) && device.getNote().length() > 250
                            || !StringUtils.isEmpty(device.getVendor()) && device.getVendor().length() > 25
                            || !StringUtils.isEmpty(device.getVersion()) && device.getVersion().length() > 25
                    ) {
                        throw new Exception("设备信息字段超出长度（备注和地址长度最大250，其他最大25），请检查。");
                    }

                    if (device.getLongitude() != null) {
                        checkDegree(device.getLongitude(), true);
                    }
                    if (device.getLatitude() != null) {
                        checkDegree(device.getLatitude(), false);
                    }


                    //补充projectid， devicetypeid，productlineId
                    Project project = projectMap.get(device.getProjectName());
                    if (project == null) {
                        throw new Exception("该设备关联的项目尚未创建。");
                    }
                    device.setProjectId(project.getId());
                    device.setProductLineId(project.getProductLineId());
                    if (deviceTypeMap.get(device.getDeviceTypeName()) == null) {
                        throw new Exception("该设备关联的设备类型尚未创建。");
                    }
                    device.setDeviceTypeId(deviceTypeMap.get(device.getDeviceTypeName()));
                    Device oldDevice = null;

                    try {
                        oldDevice = deviceService.findById(device.getId());
                    } catch (javax.persistence.NoResultException e) {
                    }

                    if (oldDevice == null) {
                        deviceService.create(device);
                    } else {
                        oldDevice.setAddrs(device.getAddrs());
                        oldDevice.setDeviceTypeId(device.getDeviceTypeId());
                        oldDevice.setLatitude(device.getLatitude());
                        oldDevice.setLongitude(device.getLongitude());
                        oldDevice.setName(device.getName());
                        oldDevice.setNote(device.getNote());
                        oldDevice.setProductLineId(device.getProductLineId());
                        oldDevice.setProjectId(device.getProjectId());
                        oldDevice.setVendor(device.getVendor());
                        oldDevice.setVersion(device.getVersion());
                        deviceService.modify(oldDevice);
                    }
                    partSuccess = true;
                } catch (Exception e) {
                    LogOperator.err(loggerName, "导入Device " + device.getName() + "失败：", e);
                    sb.append("导入Device " + device.getName() + "失败：" + e.getMessage() + ";<br> ");
                }

            }

        } catch (Exception e) {
            LogOperator.err(loggerName, "import device error", e);
            return ResultUtil.error(Result.CODE_FAILED, e.getMessage());
        }
        if (sb.length() == 0) {
            writeLog(LogConstant.OPER_IMPORT, Result.CODE_SUCCESS, fileName);
            return ResultUtil.success();
        } else {
            if (partSuccess) {
                writeLog(LogConstant.OPER_IMPORT, Result.CODE_PARTIAL_SUCCESS, sb.toString());
                return ResultUtil.error(Result.CODE_PARTIAL_SUCCESS, sb.toString());
            } else {
                writeLog(LogConstant.OPER_IMPORT, Result.CODE_FAILED, sb.toString());
                return ResultUtil.error(Result.CODE_FAILED, sb.toString());
            }
        }
    }

    @ApiOperation(value = "导出设备列表", notes = "将系统中的设备信息导出到excel文件。")
    // @PreAuthorize("hasAuthority('queryDevice')")
    @RequestMapping(value = {"/exportDeviceList"}, method = RequestMethod.GET)
    public String exportDeviceList(HttpServletRequest request, HttpServletResponse response) {
        String date = DateFormatUtils.format(new Date(), "yyyyMMddHHmmss");
        String fileName = "Device_" + date + ".xlsx";
        List<Device> deviceList = this.deviceService.findAll();

        try {
            ExportExcel exportExcel = new ExportExcel("设备信息", Device.class);
            exportExcel.setDatePattern("yyyy-MM-dd");
            exportExcel.setDataList(deviceList).write(response, fileName).dispose();
            writeLog(LogConstant.OPER_EXPORT, Result.CODE_SUCCESS, fileName);
        } catch (IOException e) {
            LogOperator.err(loggerName, "export device info failed!", e);
            writeLog(LogConstant.OPER_EXPORT, Result.CODE_FAILED, e.getMessage());
        }
        return null;
    }

    private void checkDegree(double degree, boolean isLongititude) throws Exception {
        if (isLongititude) {
            if (degree > 180 || degree < -180) {
                throw new Exception("经度信息范围不正确（-180~180）,请检查。");

            }

        } else {

            if (degree > 90 || degree < -90) {

                throw new Exception("纬度信息范围不正确（-90~90）,请检查。");

            }

        }
        String dregeeStr = String.valueOf(degree);
        int dotIndex = dregeeStr.indexOf(".");

        if (dotIndex != -1) {

            String pointAfterLength = dregeeStr.substring(dotIndex + 1);

            if (pointAfterLength.isEmpty() || pointAfterLength.length() > 7) {

                throw new Exception("纬度信息的小数点部分支持最多7位,请检查。");

            }

        }
    }

    /**
     * @param result
     * @param detail
     */
    private void writeLog(String op, String result, String detail) {
        OperLog operLog = new OperLog();
        operLog.setOperateTime(new Date());
        operLog.setModule(LogConstantEx.MODULE_PROJECT);
        operLog.setCommand(op);
        operLog.setResult(result);
        operLog.setOperateUser(ServiceUtils.getLoginUser().getUsername());
        operLog.setDetail(detail);
        operLogService.writeLog(operLog);
    }
}
